#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include "point.h"
#include "polygon.h"
#include "rayintersections.h"
#include "grid.h"
#include "segment.h"
#include "generatepoints.h"
#include "segmentrasterization.h"

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <iosfwd>
#include <vector>
#include <algorithm>
#include <stdlib.h>


BOOST_AUTO_TEST_CASE(test_grid_inside)
{
    std::vector<std::string> files;
    files.push_back("../input_data/1.map");
    files.push_back("../input_data/2.map");
    files.push_back("../input_data/4.map");
    files.push_back("../input_data/9.map");
    files.push_back("../input_data/10.map");

    for(size_t j = 0; j < files.size(); ++j)
    {
        std::ifstream in(files[j].c_str());
        polygon_data_t polygon_data;
        in >> polygon_data;

        std::vector<point_t> points = random_points(top_left_corner(polygon_data), bottom_right_corner(polygon_data));
        polygon_t polygon = get_polygon(polygon_data);

        grid_t grid(polygon, 800);
        int error_counter = 0;
        for(size_t i = 0; i < points.size(); ++i)
        {
            if(grid.inside(points[i]) != belongs(points[i], polygon))
                ++error_counter;
        }
        if (error_counter > 0)
        {
            std::ostringstream s;
            s << files[j] << ":: Errors found: " << error_counter;
            BOOST_ERROR(s.str().c_str());
        }
    }
}

BOOST_AUTO_TEST_CASE(test_segment_less)
{
    for(char c = '0'; c < '2'; ++c)
    {
        std::string file("../input_data/input_testLessByAngle");
        file.push_back(c);
        std::ifstream in(file.c_str());
        segment_t base;
        in >> base.first >> base.second;

        segment_t seg;
        std::vector<segment_t> segments;
        while(in >> seg.first >> seg.second)
        {
            segments.push_back(seg);
        }  
        std::vector<segment_t> origin(segments.size());
        std::copy(segments.begin(), segments.end(), origin.begin());

        for(size_t j = 0; j < 2; ++j)
        {
            std::random_shuffle(segments.begin(), segments.end());
            less_by_angle less(base);

            std::sort(segments.begin(), segments.end(), less);
            BOOST_REQUIRE(std::equal(segments.begin(), segments.end(), origin.begin()));
        
            std::reverse(origin.begin(), origin.end());
            std::swap(base.first, base.second);
        }
    }
}

BOOST_AUTO_TEST_CASE(test_ray_t)
{
    std::ifstream in("../input_data/input_testRayIntersections");
    polygon_data_t polygon_d;
    in >> polygon_d;
    std::string buff;
    polygon_t polygon = get_polygon(polygon_d);

    while (std::getline(in, buff))
    {
        point_t ray_start;
        bool expected;
        std::stringstream ss(buff);
        ss >> ray_start >> expected;

        if(belongs(ray_start, polygon) != expected)
        {
            std::ostringstream s;
            s << ray_start;
            BOOST_ERROR(s.str().c_str());
        }
    }
}

BOOST_AUTO_TEST_CASE(test_grid_t)
{
    std::ifstream in("../input_data/input_testBuildGrid");
    double cell_size = 0;
    polygon_t polygon;
    grid_t expected;
    in >> cell_size >> polygon >> expected;

    BOOST_CHECK_EQUAL(grid_t(polygon, cell_size), expected);
}

BOOST_AUTO_TEST_CASE(test_contour_direction)
{
    std::ifstream in("../input_data/input_testDirection");
    int result = 0;
    contour_t contour;
    char d;
    int i = 0;
    
    while ((i++) < 6 && in >> result >> d >> contour)
    {
        direction_t answer = (result == 0) ? CLOCKWISE : COUNTERCLOCKWISE;
        if(d == ';')
        {
            direction_t dir = contour.direction();            
            BOOST_CHECK_EQUAL(dir, answer);

            answer = (answer == CLOCKWISE) ? COUNTERCLOCKWISE : CLOCKWISE;
            contour.direction(direction_t(answer));
            dir = contour.direction();
            
            BOOST_CHECK_EQUAL(dir, answer);            
        } else break;
    }
}

BOOST_AUTO_TEST_CASE(test_point_position)
{
    segment_t seg(point_t(3, 0), point_t(4, 10));
    BOOST_CHECK_EQUAL(point_position(seg, point_t(0, 0)), 0);
    BOOST_CHECK_EQUAL(point_position(seg, point_t(11, 0)), 1);
    BOOST_CHECK_EQUAL(point_position(seg, point_t(3, 0)), 2);
    
    seg = segment_t(point_t(4, 10), point_t(3, 0));
    BOOST_CHECK_EQUAL(point_position(seg, point_t(0, 0)), 1);
    BOOST_CHECK_EQUAL(point_position(seg, point_t(11, 0)), 0);
    BOOST_CHECK_EQUAL(point_position(seg, point_t(3, 0)), 2);
}

BOOST_AUTO_TEST_CASE(test_grid_centers_positions)
{
    for(char c = '0'; c < '2'; ++c)
    {
    	std::string file("../input_data/input_testCentersPositions");
	file.push_back(c);

    	std::ifstream in(file.c_str());

    	polygon_t polygon;
    	size_t cell_size;
    	in >> cell_size >> polygon;
    	grid_t grid(polygon, cell_size);

    	point_t point;
    	bool position;
    	size_t i = 0;
    
    	while (in >> point >> position)
    	{
        	BOOST_CHECK_EQUAL(grid[i].center(), point);
        	BOOST_CHECK_EQUAL(grid[i].center_in(), position);
        	++i;
    	}
   }
}

BOOST_AUTO_TEST_CASE(test_segment_intersection)
{
    std::vector<segment_t> segments;
    segments.push_back(segment_t(point_t(0, 0), point_t(10, 2)));
    segments.push_back(segment_t(point_t(5, 0), point_t(10, 20)));
    segments.push_back(segment_t(point_t(13, 0), point_t(10, 20)));

    point_t point;
    BOOST_CHECK_EQUAL(intersects(segments[0], segments[1], point), true);
    BOOST_CHECK_EQUAL(intersects(segments[0], segments[2], point), false);
    BOOST_CHECK_EQUAL(intersects(segments[1], segments[0], point), true);
    BOOST_CHECK_EQUAL(intersects(segments[2], segments[0], point), false);
}

BOOST_AUTO_TEST_CASE(test_rasterization)
{
    std::ifstream in("../input_data/1.map");
    polygon_t polygon;
    in >> polygon;
    
    inserter f;
    for(size_t i = 0; i < polygon.size(); ++i)
    {
        for(size_t j = 1; j < polygon[i].size(); ++j)
        {
            segment_t seg(polygon[i][j - 1], polygon[i][j]);
            std::vector<cell_t> res = rasterize(seg, 1000, boost::ref(f));
            
            grid_t grid(1000);
            grid.visit(seg.first, seg.second, boost::ref(f));

            std::vector<cell_t> grid_res;
            for(size_t k = 0; k < grid.size(); ++k)
            {
                if(!grid[k].empty())
                {
                    BOOST_CHECK_EQUAL(std::find(grid_res.begin(), grid_res.end(), grid[k]) != grid_res.end(), false);
                    grid_res.push_back(grid[k]);
                    BOOST_CHECK_EQUAL(std::find(res.begin(), res.end(), grid[k]) != res.end(), true);
                }
            }
            BOOST_CHECK_EQUAL(grid_res.size(), res.size());
        }
    }
}
